﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;

using OtfPG.Objects;

namespace OtfPG.Core
{
    public class Generation
    {
        #region Properties

        private char[] SpecialCharacters = { '|', '@', '?', '#', '~', '$', '%', '^', '*', '£' };

        private string _userPassPhrase = string.Empty;
        public string UserPassPhrase
        {
            set
            {
                _userPassPhrase = value;
            }
        }

        private string _generatedPassword = string.Empty;
        public string GeneratedPassword
        {
            get
            {
                return _generatedPassword;
            }
        }

        private Options _passwordOptions = null;
        private Options PasswordOptions
        {
            get
            {
                return _passwordOptions;
            }
        }

        #endregion

        public Generation() { }

        public void GeneratePassword(string password, Options passwordOptions)
        {
            _passwordOptions = passwordOptions;

            // Generate password from userString
            //_generatedPassword = password;

            MD5 md5Hash = MD5.Create();

            // Convert the input string to a byte array and compute the hash. 
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(password));

            // Create a new Stringbuilder to collect the bytes 
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data  
            // and format each one as a hexadecimal string. 
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            string prGeneratedPassword = string.Empty;
            List<string> splitHash = new List<string>();

            SplitGeneratedHash(sBuilder.ToString(), out splitHash);
            ApplyPasswordRules(splitHash, out prGeneratedPassword);

            // Return the hexadecimal string. 
            _generatedPassword = prGeneratedPassword;
        }

        private void SplitGeneratedHash(string generatedHash, out List<string> splitHash)
        {
            splitHash = new List<string>();

            StringBuilder currentSplit = new StringBuilder();
            Enums.CharacterType characterType = Enums.CharacterType.Letter;

            foreach (char character in generatedHash)
            {
                // If your at the beginning or starting new block
                if (currentSplit.Length == 0)
                {
                    characterType = CharacterTypeChecker(character);
                    currentSplit.Append(character);
                    continue;
                }

                if (CharacterTypeChecker(character) != characterType)
                {
                    splitHash.Add(currentSplit.ToString());
                    currentSplit.Clear();

                    characterType = CharacterTypeChecker(character);
                    currentSplit.Append(character);

                    continue;
                }

                currentSplit.Append(character);
            }

            // Catch the final character(s)
            splitHash.Add(currentSplit.ToString());
            currentSplit.Clear();
        }

        private void ApplyPasswordRules(List<string> splitHash, out string postRulesetGeneratedPassword)
        {
            postRulesetGeneratedPassword = string.Empty;
            StringBuilder firstPassPassword = new StringBuilder();

            foreach (string currentHashItem in splitHash)
            {
                string resultantItem = currentHashItem;

                long numericValue = 0;
                if (long.TryParse(resultantItem, out numericValue) && resultantItem.Length > 1)
                    resultantItem = currentHashItem[currentHashItem.Length - 1].ToString();

                firstPassPassword.Append(resultantItem);
            }

            int upperCounter = 0;
            for (int i = 0; i < firstPassPassword.Length; i++)
            {
                string c = firstPassPassword[i].ToString();

                if (Char.IsLetter(c,0))
                {
                    upperCounter++;

                    if (upperCounter % 3 == 0)
                    {
                        char[] upperedChar = firstPassPassword[i].ToString().ToUpper().ToCharArray();
                        firstPassPassword[i] = upperedChar[0];
                    }
                }

                if (Char.IsNumber(c,0))
                {
                    if (PasswordOptions.UseSpecialCharacters && firstPassPassword.ToString().Contains(c) && firstPassPassword.ToString().Count(currentC => currentC.ToString() == c) > 1)
                        firstPassPassword[i] = SpecialCharacters[int.Parse(c)];
                }
            }

            postRulesetGeneratedPassword = firstPassPassword.ToString();

            if (PasswordOptions.ReduceLength && PasswordOptions.PasswordLength.HasValue)
                postRulesetGeneratedPassword = postRulesetGeneratedPassword.Substring(0, PasswordOptions.PasswordLength.Value);
        }

        #region Private Helper Methods

        private Enums.CharacterType CharacterTypeChecker(char c)
        {
            if (Char.IsNumber(c))
                return Enums.CharacterType.Number;

            return Enums.CharacterType.Letter;
        }

        #endregion
    }
}
